#ifndef __RLOG_H__
#define __RLOG_H__

#include "rlog_adapter.h"

/* Rlog error code */
typedef int rlog_err_t;

#define RLOG_EOK                        0
#define RLOG_ERR                        1

/* Rlog level flag */
typedef enum
{
    RLOG_LVL_ASSERT = 0,
    RLOG_LVL_ERROR,
    RLOG_LVL_WARNING,
    RLOG_LVL_INFO,
    RLOG_LVL_DEBUG,
    RLOG_LVL_VERBOSE,
    RLOG_LVL_MAX,
} rlog_lvl_t;

/* Rlog fomat flag */
#define RLOG_FMT_LVL                    (1 << 0)
#define RLOG_FMT_TAG                    (1 << 1)
#define RLOG_FMT_TIME                   (1 << 2)
#define RLOG_FMT_DIR                    (1 << 3)
#define RLOG_FMT_FUNC                   (1 << 4)
#define RLOG_FMT_LINE                   (1 << 5)

#define RLOG_OSC_CH_MAX                 4

typedef struct
{
    float ch[RLOG_OSC_CH_MAX];
}rlog_osc_data;

typedef void (*rlog_assert_hook_t)(const char* expr, const char* func, size_t line);
typedef void (*rlog_output_hook_t)(const char *log, uint16_t log_len);

extern rlog_assert_hook_t rlog_assert_hook;

#if RLOG_ASSERT_ENABLE
    #define rlog_assert(EXPR)                                                                   \
    if (!(EXPR))                                                                                \
    {                                                                                           \
        if (rlog_assert_hook == NULL) {                                                         \
            rlog_a("rlog", "(%s) has assert failed at %s:%ld.", #EXPR, __FUNCTION__, __LINE__); \
            while (1);                                                                          \
        } else {                                                                                \
            rlog_assert_hook(#EXPR, __FUNCTION__, __LINE__);                                    \
        }                                                                                       \
    }
#else
    #define rlog_assert(EXPR)                   ((void)0);
#endif

#if RLOG_OUTPUT_ENABLE
    #define RLOG_ASSERT(tag, ...) \
        rlog_fmt_print(RLOG_LVL_ASSERT, tag, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__)
    #define RLOG_ERROR(tag, ...) \
        rlog_fmt_print(RLOG_LVL_ERROR, tag, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__)
    #define RLOG_WARNING(tag, ...) \
        rlog_fmt_print(RLOG_LVL_WARNING, tag, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__)
    #define RLOG_INFO(tag, ...) \
        rlog_fmt_print(RLOG_LVL_INFO, tag, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__)
    #define RLOG_DEBUG(tag, ...) \
        rlog_fmt_print(RLOG_LVL_DEBUG, tag, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__)
    #define RLOG_VERBOSE(tag, ...) \
        rlog_fmt_print(RLOG_LVL_VERBOSE, tag, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__)
#else
    #define RLOG_ASSERT(tag, ...)
    #define RLOG_ERROR(tag, ...)
    #define RLOG_WARNING(tag, ...)
    #define RLOG_INFO(tag, ...)
    #define RLOG_DEBUG(tag, ...)
    #define RLOG_VERBOSE(tag, ...)
#endif /* RLOG_OUTPUT_ENABLE */

/**
 * @brief RLog init function
 * 
 * @return init result
 */
rlog_err_t rlog_init(void);

/**
 * @brief RLog deinit function
 * 
 */
void rlog_deinit(void);

/**
 * @brief RLog output enable
 * 
 * @param enable true: enable output, false: disable output 
 */
void rlog_enable(bool enable);

/**
 * @brief RLog output color enable
 * 
 * @param enable true: enable output color, false: disable output color
 */
void rlog_color_enable(bool enable);

/**
 * @brief RLog level output format setting
 * 
 * @param level  log level
 * @param format log format
 */
void rlog_level_fmt_set(rlog_lvl_t level, int format);

/**
 * @brief RLog level output format setting
 * 
 * @param level  log level
 * @param format log format
 * 
 * @return result true: supported format, false: unsupported format
 */
bool rlog_level_fmt_get(rlog_lvl_t level, int format);

/**
 * @brief RLog filter the content of the log level
 * 
 * @param level  log level
 */
void rlog_level_filter_set(rlog_lvl_t level);

/**
 * @brief RLog level output format setting
 * 
 * @return result true: supported format, false: unsupported format
 */
rlog_lvl_t rlog_level_filter_get(void);

/**
 * RLog setting assert hook
 * 
 * @param hook hook function
 */
void rlog_assert_set_hook(rlog_assert_hook_t hook);

/**
 * @brief RLog setting output hook
 * 
 * @param hook hook function
 */
void rlog_output_set_hook(rlog_output_hook_t hook);

/**
 * @brief RLog print
 * 
 * @param fmt log output format
 * @param ... args
 */
void rlog_print(const char *fmt, ...);

/**
 * @brief RLog format print
 * 
 * @param level  log output level
 * @param tag    log output tag
 * @param file   current file name
 * @param func   current function name
 * @param line   current line number
 * @param fmt    log output format
 * @param ...    args
 */
void rlog_fmt_print(rlog_lvl_t level, const char *tag, const char *file, 
                    const char *func, const int line, const char *fmt, ...);

/**
 * @brief RLog hexdump print
 * 
 * @param title    hexdump title name
 * @param buff     hexdump buff
 * @param buff_len hexdump buff length
 */
void rlog_hexdump_print(const char *title, uint8_t *buff, uint16_t buff_len);

/**
 * @brief 
 * 
 * @param data osc data
 */
void rlog_osc_print(rlog_osc_data *data);

#if !defined(assert)
    #define assert                          rlog_assert
#endif

#if !defined(RLOG_TAG)
    #define RLOG_TAG                        "RLOG"
#endif
#if !defined(RLOG_LVL)
    #define RLOG_LVL                        RLOG_LVL_VERBOSE
#endif

#define RLOG_FMT_ALL (RLOG_FMT_LVL | RLOG_FMT_TAG | RLOG_FMT_TIME | RLOG_FMT_DIR | RLOG_FMT_FUNC | RLOG_FMT_LINE)  

#define RLOG_PRINTF(...)                    rlog_print(__VA_ARGS__)

#define RLOG_HEXDUMP(title, buff, len)      rlog_hexdump_print(title, buff, len)

#if (RLOG_OUTPUT_LEVEL >= RLOG_LVL_ASSERT && RLOG_LVL >= RLOG_LVL_ASSERT)
    #define rlog_a(...)                     RLOG_ASSERT(RLOG_TAG, __VA_ARGS__)
#else
    #define rlog_a(...)
#endif
#if (RLOG_OUTPUT_LEVEL >= RLOG_LVL_ERROR && RLOG_LVL >= RLOG_LVL_ERROR)
    #define rlog_e(...)                     RLOG_ERROR(RLOG_TAG, __VA_ARGS__)
#else
    #define rlog_e(...)
#endif
#if (RLOG_OUTPUT_LEVEL >= RLOG_LVL_WARNING && RLOG_LVL >= RLOG_LVL_WARNING)
    #define rlog_w(...)                     RLOG_WARNING(RLOG_TAG, __VA_ARGS__)
#else
    #define rlog_w(...)
#endif
#if (RLOG_OUTPUT_LEVEL >= RLOG_LVL_INFO && RLOG_LVL >= RLOG_LVL_INFO)
    #define rlog_i(...)                     RLOG_INFO(RLOG_TAG, __VA_ARGS__)
#else
    #define rlog_i(...)
#endif
#if (RLOG_OUTPUT_LEVEL >= RLOG_LVL_DEBUG && RLOG_LVL >= RLOG_LVL_DEBUG)
    #define rlog_d(...)                     RLOG_DEBUG(RLOG_TAG, __VA_ARGS__)
#else
    #define rlog_d(...)
#endif
#if (RLOG_OUTPUT_LEVEL >= RLOG_LVL_VERBOSE && RLOG_LVL >= RLOG_LVL_VERBOSE)
    #define rlog_v(...)                     RLOG_VERBOSE(RLOG_TAG, __VA_ARGS__)
#else
    #define rlog_v(...)
#endif

#endif
